﻿/*
   Copyright 2011 Softisis

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.Linq.Expressions;

namespace FOM.Interfaces
{
    /// <summary>
    /// Contract for mapping, the generic way for intellisens.
    /// </summary>
    /// <remarks>Copyright Softisis 2011 (France)</remarks>
    /// <typeparam name="TSrc">Source type object</typeparam>
    /// <typeparam name="TDst">Destination type object</typeparam>
    public interface IMappingNode<TSrc, TDst> : INode
    {
        /// <summary>
        /// Create a child mapping on "this" mapping.
        /// </summary>
        /// <typeparam name="TPropertyS">Type of source property.</typeparam>
        /// <typeparam name="TPropertyD">Type of targeted property</typeparam>
        /// <param name="expsrc">Expression pointing at the source property</param>
        /// <param name="expdst">Expression pointing at the target property</param>
        /// <returns>A new mapping for these two properties.</returns>
        IMappingNode<TPropertyS, TPropertyD> Map<TPropertyS , TPropertyD>(Expression<Func<TSrc, TPropertyS>> expsrc,
                                                Expression<Func<TDst, TPropertyD>> expdst);

        /// <summary>
        /// Create a child mapping on "this" mapping. The source and destination are the same.
        /// </summary>
        /// <typeparam name="TProperty">Property of the source/destination</typeparam>
        /// <param name="expr">Expression to source and destination property</param>
        /// <returns>A new mapping for these two properties.</returns>
        IMappingNode<TProperty, TProperty> Map<TProperty>(Expression<Func<TSrc, TProperty>> expr);


        /// <summary>
        /// Define some exclusion path. Can be usefull with IConverter that deals with massive copies.
        /// Exclusion is taken prior to a Map.
        /// </summary>
        /// <typeparam name="TPropertyS">Type of source property.</typeparam>
        /// <typeparam name="TPropertyD">Type of targeted property</typeparam>
        /// <param name="expsrc">Expression pointing at the source property</param>
        /// <param name="expdst">Expression pointing at the target property</param>
        /// <returns>"this"</returns>
        IMappingNode<TSrc, TDst> Exclude<TPropertyS, TPropertyD>(Expression<Func<TSrc, TPropertyS>> expsrc,
                                                Expression<Func<TDst, TPropertyD>> expdst);


        /// <summary>
        /// Define some exclusion path on the same path for source and target object.
        /// </summary>
        /// <typeparam name="TPropertyS">Type of source property.</typeparam>
        /// <param name="expsrc">Expression pointing at the source property</param>
        /// <returns>"this"</returns>
        IMappingNode<TSrc, TDst> Exclude<TPropertyS>(Expression<Func<TSrc, TPropertyS>> expsrc);

        /// <summary>
        /// Tells the kernel to have an new instance of the pointed object
        /// to be created for this mapping, instead of using the original object.
        /// On the root mapping, this is also allowed. But when the mapping is run, if a destination
        /// object is provided, it will be ignored.
        /// This call is tha same as NewInstance(false)
        /// </summary>²
        /// <returns>"This"</returns>
        IMappingNode<TSrc, TDst> NewInstance();


        /// <summary>
        /// Tells the kernel to have an new instance of the pointed object
        /// to be created for this mapping, instead of using the original object.
        /// On the root mapping, this is also allowed. But when the mapping is run, if a destination
        /// object is provided, it will be ignored.
        /// The parameter reuseCached tells the kernel wether or not an existing previously created 
        /// instance must be reused or not. (for the same source reference).
        /// </summary>
        /// <param name="reuseCached">If true, the instance is taken from cache if not already created, or put in cache for reuse.
        /// Use this parameter to maintain references.</param>
        /// <returns>"This"</returns>
        IMappingNode<TSrc, TDst> NewInstance(bool reuseCached);



        /// <summary>
        /// Go into the objects. It does not create a mapping, but a new startpoint from the path.
        /// NewInstance and OverrideingTypes can be applied on the "GoTo" returned node.
        /// </summary>
        /// <typeparam name="TPropertyS">Source property type</typeparam>
        /// <typeparam name="TPropertyD">Target proeprty type</typeparam>
        /// <param name="expsrc">Expression that points to the source property</param>
        /// <param name="expdst">Expression that points to the target property</param>
        /// <returns>The node to the prop source/target pair</returns>
        IMappingNode<TPropertyS, TPropertyD> GoTo<TPropertyS, TPropertyD>(Expression<Func<TSrc, TPropertyS>> expsrc,
                                                Expression<Func<TDst, TPropertyD>> expdst);


        /// <summary>
        /// Tells the kernel to use another type instead of the initial target one.
        /// </summary>
        /// <returns>"This"</returns>
        IMappingNode<TSrc, TReplacement> OverrideTargetType<TReplacement>();


        /// <summary>
        /// Tells the kernel to use another type instead of the initial source one.
        /// </summary>
        /// <returns>"This"</returns>
        IMappingNode<TReplacement, TDst> OverrideSourceType<TReplacement>();

        /// <summary>
        /// Tells the kernel that a specific converter must be used to translate the source
        /// to destination.
        /// </summary>
        /// <param name="converter">The converter to use to convert source to target. CF. IConverter for more informations.</param>
        /// <returns>"this"</returns>
        IMappingNode<TSrc, TDst> UseConverter(IConverter converter);

        /// <summary>
        /// Make the mapping a conditonnal mapping. If ICondition.CanApplyMapping returns true, 
        /// the target value is affected. Otherwise, it's ignored, the target is left "as is".
        /// </summary>
        /// <param name="cond">This condition implementation</param>
        /// <returns>True if the mapping can be applied, false otherwise.</returns>
        IMappingNode<TSrc, TDst> UseCondition(ICondition cond);

        
        /// <summary>
        /// Reverse the mapping. Applyed on a node &lt;TSrc, TDst&gt;, the result is a node &lt;TDst, TSrc&gt;.
        /// This methods is recursive. The name given on a root node is [name]Reverse.
        /// </summary>
        ///  <returns>The reversed node</returns>
        IMappingNode<TDst, TSrc> Reverse();


        /// <summary>
        /// move a mapping. If the mapping does not exists, well ... nothing happens.
        /// </summary>
        /// <typeparam name="TPropertyS"></typeparam>
        /// <typeparam name="TPropertyD"></typeparam>
        /// <param name="expsrc"></param>
        /// <param name="expdst"></param>
        bool Remove<TPropertyS, TPropertyD>(Expression<Func<TSrc, TPropertyS>> expsrc,
                                        Expression<Func<TDst, TPropertyD>> expdst);
    }
}